/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 * The package containing the main classes for the game screens and their
 * controllers
 */
package angrypainters.application;

import angrypainters.components.GameModel;
import angrypainters.components.ImageBounds;
import angrypainters.components.Message;
import angrypainters.components.MessageType;
import angrypainters.components.NetworkManager;
import angrypainters.components.Player;
import angrypainters.components.ResourceManager;
import angrypainters.components.ScreensManager;
import java.net.URL;
import java.util.Random;
import java.util.ResourceBundle;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.PathTransition;
import javafx.animation.Timeline;
import javafx.beans.binding.Bindings;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelReader;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.text.Text;
import javafx.util.Duration;

/**
 * Game Controller class that has all the methods and properties to handle the
 * game logic
 *
 * @author A7med
 */
public class GameScreenController implements Initializable {

    @FXML
    StackPane guestPane;
    @FXML
    AnchorPane gamePane;
    @FXML
    StackPane hostPane;
    @FXML
    ImageView mark;
    @FXML
    ImageView hostHouse;
    @FXML
    ImageView guestHouse;
    private ScreensManager sc;
    private ResourceManager manager;
    PathTransition pt;
    private NetworkManager netManager;
    @FXML
    Text player;
    @FXML
    Text ownScore;
    @FXML
    Text opponentScore;
    @FXML
    Text result;
    @FXML
    Arc hostBaloon;
    @FXML
    Arc guestBaloon;
    GameModel model;

    /**
     * getter method for the network manager
     *
     * @return network manager instance
     */
    public NetworkManager getNetManager() {
        return netManager;
    }

    /**
     * setter for network manager
     *
     * @param netManager
     */
    public void setNetManager(NetworkManager netManager) {
        this.netManager = netManager;
    }

    /**
     * getter method for the game model
     *
     * @return game instance
     */
    public GameModel getModel() {
        return model;
    }

    /**
     * setter for the game model, also binds the score labels to the score
     * property in the model
     *
     * @param model
     */
    public void setModel(GameModel model) {
        this.model = model;

        ownScore.textProperty().bind(Bindings.concat("Turns Remaining: ", model.getOwnScoreProperty()));
        opponentScore.textProperty().bind(Bindings.concat("Turns Remaining: ", model.getOpponentScoreProperty()));

    }

    /**
     * Initializes the controller class.
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO

    }

    /**
     * Start the receiving socket
     */
    public void startListning() {
        netManager.recieveData();
    }

    /**
     * getter for screen manager
     *
     * @return screen manager instance
     */
    public ScreensManager getSc() {
        return sc;
    }

    /**
     * setter for screen manager
     *
     * @param sc the screen manager
     */
    public void setSc(ScreensManager sc) {
        this.sc = sc;
    }

    /**
     * getter for the resource manager
     *
     * @return resource manager instance
     */
    public ResourceManager getManager() {
        return manager;
    }

    public void setManager(ResourceManager manager) {
        this.manager = manager;
    }

    /**
     * handler method for keyboard press events
     *
     * @param event the passed keyboard event
     */
    public void handleKeyPress(KeyEvent event) {
        if (event.getCode() == KeyCode.ESCAPE) {
            sc.setScreen("start");
        }
        if (event.getCode() == KeyCode.R) {
            if (model.getCurrentPlayer() == model.getPlayerType()) {
                hostBaloon.setVisible(true);
                hostBaloon.setTranslateX(0);
                hostBaloon.setTranslateY(0);
                pt = new PathTransition();
                pt.setPath(createPath(Player.guest));
                pt.setNode(guestPane);
                pt.setDuration(Duration.seconds(5));
                pt.setAutoReverse(true);
                pt.setCycleCount(Timeline.INDEFINITE);
                pt.play();
            }
        }
    }

    /**
     * mouse click event handler
     *
     * @param event the mouse event
     */
    public void handleMouseclicks(MouseEvent event) {
        if (model.getCurrentPlayer() == model.getPlayerType()) {

            pt.stop();
            moveBaloon(hostBaloon, event.getSceneX(), event.getSceneY());
            if (guestPane.getBoundsInParent().contains(mark.getLayoutX() + mark.getFitWidth() / 2, mark.getLayoutY() + mark.getFitHeight() / 2)) {
                ImageBounds b = new ImageBounds(mark.getLayoutX() + mark.getFitWidth() / 2 - 3, mark.getLayoutY() - mark.getFitHeight() / 2, mark.getFitWidth(), mark.getFitHeight());
                BoundingBox box = new BoundingBox(b.getX(), b.getY(), b.getWidth(), b.getHeight());
                WritableImage newImage = writeToImage(model.getOwnColor(), box, guestHouse.getImage());

                guestHouse.setImage(newImage);
                netManager.sendData(new Message(MessageType.HouseImage, b));
                model.setHits(model.getHits()+1);
            }

            model.setOwnScore(model.getOwnScore() - 1);
            if (model.getOwnScore() == 0 && model.getOpponentScore() == 0) {
                checkForWin();
            } else {
                netManager.sendData(new Message(MessageType.SwitchPlayer, ""));
                model.setCurrentPlayer(model.getCurrentPlayer() == Player.guest ? Player.host : Player.guest);
            }
        }
    }

    /**
     * mouse moves event handler
     *
     * @param event the mouse move event
     */
    public void handleMouseMoves(MouseEvent event) {
        if (model.getCurrentPlayer() == model.getPlayerType()) {
            mark.setLayoutX(event.getSceneX() - mark.getFitWidth() / 2);
            mark.setLayoutY(event.getSceneY() - mark.getFitHeight() / 2);
        }

    }

    /**
     * creates a random path to animate the aim area along it
     *
     * @param player
     * @return the created path
     */
    private Path createPath(Player player) {
        int[] points = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
        double housepositionX = player == Player.guest ? (guestPane.getBoundsInLocal().getMinX() + guestPane.getBoundsInLocal().getWidth() / 2) : hostPane.getTranslateY();
        double housepositionY = guestPane.getBoundsInLocal().getMinY() + guestPane.getBoundsInLocal().getHeight() / 2;

        Path p = new Path();

        p.getElements().add(new MoveTo(housepositionX, housepositionY));

        shuffleArray(points);

        for (int i : points) {
            System.out.println("i=" + i);

            int row = i - 3;
            if (row <= 0) {
                row = 1;
            } else if (row > 0 && row < 4) {
                row = 2;
            } else {
                row = 3;
            }
            int column = i % 3;
            if (column == 0) {
                column = 3;
            }
            column--;
            row--;
            p.getElements().add(new LineTo(housepositionX + (column * 100), housepositionY + (row * 75)));
        }
        return p;

    }

    /**
     * shuffle the content of an array
     *
     * @param ar the array to shuffle
     */
    static void shuffleArray(int[] ar) {
        Random rnd = new Random();
        for (int i = ar.length - 1; i > 0; i--) {
            int index = rnd.nextInt(i + 1);
            int a = ar[index];
            ar[index] = ar[i];
            ar[i] = a;
        }
    }

    /**
     * overwrites the guestColor information of an image with the passed
     * guestColor inside the passed bounds
     *
     * @param c new guestColor
     * @param bound bound to change inside it
     * @param image original image
     * @return
     */
    private WritableImage writeToImage(Color c, Bounds bound, Image image) {
        WritableImage wImage = new WritableImage(
                (int) image.getWidth(),
                (int) image.getHeight());
        PixelWriter pixelWriter = wImage.getPixelWriter();
        PixelReader pixelReader = image.getPixelReader();
        PixelReader r = wImage.getPixelReader();
        for (int readY = 0; readY < image.getHeight(); readY++) {
            for (int readX = 0; readX < image.getWidth(); readX++) {
                Color color = pixelReader.getColor(readX, readY);

                pixelWriter.setColor(readX, readY, color);
            }
        }
        for (int readY = (int) bound.getMinY(); readY < (int) bound.getMaxY(); readY++) {
            for (int readX = (int) bound.getMinX(); readX < (int) bound.getMaxX(); readX++) {
                Color color = r.getColor(readX, readY);

                if (color.equals(model.getOpponentColor())) {

                    pixelWriter.setColor(readX, readY, c);

                }

            }
        }

        return wImage;
    }

    private WritableImage writeToHostImage(Color c, Bounds bound, Image image) {
        WritableImage wImage = new WritableImage(
                (int) image.getWidth(),
                (int) image.getHeight());
        PixelWriter pixelWriter = wImage.getPixelWriter();
        PixelReader pixelReader = image.getPixelReader();
        PixelReader r = wImage.getPixelReader();
        for (int readY = 0; readY < image.getHeight(); readY++) {
            for (int readX = 0; readX < image.getWidth(); readX++) {
                Color color = pixelReader.getColor(readX, readY);

                pixelWriter.setColor(readX, readY, color);
            }
        }
        for (int readY = (int) bound.getMinY(); readY < (int) bound.getMaxY(); readY++) {
            for (int readX = (int) bound.getMinX(); readX < (int) bound.getMaxX(); readX++) {
                Color color = r.getColor(readX, readY);

                if (color.equals(model.getOwnColor())) {

                    pixelWriter.setColor(readX, readY, c);

                }

            }
        }

        return wImage;
    }

    /**
     * change host image guestColor inside the passed bounds
     *
     * @param b bounds
     */
    public void setHostImage(ImageBounds b) {

        model.setOpponentHits(model.getOpponentHits()+1);
        WritableImage i = writeToHostImage(model.getOpponentColor(), new BoundingBox(b.getX(), b.getY(), b.getWidth(), b.getHeight()), hostHouse.getImage());
        System.out.println("setting image");
        hostHouse.setImage(i);

    }

    /**
     * moves the guest balloon
     */
    public void animateGuestBaloon() {
        moveBaloon(guestBaloon, 920, 450);
    }

    /**
     * updates player label with current player
     *
     * @param text
     */
    public void updatePlayer(String text) {
        player.setText(text);
    }

    /**
     * animates the passed balloon to the new coordinates
     *
     * @param baloon
     * @param xCoordinate
     * @param yCoordinate
     */
    private void moveBaloon(final Arc baloon, double xCoordinate, double yCoordinate) {
        final double originalX = baloon.getLayoutX();
        final double originalY = baloon.getLayoutY();
        Timeline movement = new Timeline(new KeyFrame(Duration.millis(1000), new KeyValue(baloon.layoutXProperty(), xCoordinate),
                new KeyValue(baloon.layoutYProperty(), yCoordinate)));

        movement.setOnFinished(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                baloon.setVisible(false);
                baloon.setLayoutX(originalX);
                baloon.setLayoutY(originalY);
            }
        });

        movement.play();
    }

    /**
     * check the winner after finishing the balloons
     */
    private void checkForWin() {
       
        if (model.getHits() > model.getOpponentHits()) {
            setResultTesxt("You Win");
            netManager.sendData(new Message(MessageType.Result, "You Lose"));
        } else if (model.getOpponentHits() > model.getHits()) {
            setResultTesxt("You Lose");
            netManager.sendData(new Message(MessageType.Result, "You Win"));
        } else {
            setResultTesxt("Draw");
            netManager.sendData(new Message(MessageType.Result, "Draw"));
        }

    }

    /**
     * updates the result text with the new one
     *
     * @param text
     */
    public void setResultTesxt(String text) {

        result.setText(text);
        result.setVisible(true);
    }

    public void rewriteImages() {
        WritableImage hImage = new WritableImage(
                (int) hostHouse.getImage().getWidth(),
                (int) hostHouse.getImage().getHeight());
        WritableImage gImage = new WritableImage(
                (int) hostHouse.getImage().getWidth(),
                (int) hostHouse.getImage().getHeight());
        PixelWriter pixelWriter = hImage.getPixelWriter();
        PixelWriter pixelWriter2 = gImage.getPixelWriter();
        PixelReader pixelReader = hostHouse.getImage().getPixelReader();

        for (int readY = 0; readY < hImage.getHeight(); readY++) {
            for (int readX = 0; readX < hImage.getWidth(); readX++) {
                Color color = pixelReader.getColor(readX, readY);

                pixelWriter.setColor(readX, readY, color);
                pixelWriter2.setColor(readX, readY, color);

            }
        }
        for (int readY = 0; readY < (int) hImage.getHeight(); readY++) {
            for (int readX = 0; readX < (int) hImage.getWidth(); readX++) {
                Color color = pixelReader.getColor(readX, readY);

                if (color.equals(Color.WHITE)) {

                    pixelWriter.setColor(readX, readY, model.getOwnColor());
                    pixelWriter2.setColor(readX, readY, model.getOpponentColor());

                }

            }
        }
        hostHouse.setImage(hImage);
        guestHouse.setImage(gImage);
    }

}
